package com.gatewat.filter;


import com.alibaba.fastjson.JSONObject;
import com.gatewat.util.CodeFilter;
import com.gatewat.util.JwtUtil;
import com.gatewat.util.WhitelistCodeFilter;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author ZhouNing
 * @date 2021/6/15
 */

@RefreshScope
@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {

    private static final Logger log = LoggerFactory.getLogger(AuthenticationFilter.class);

    private static final String AUTHORIZE_TOKEN = "Authorization";

    private static final String REDIS_KEY = "token_key";

    /**
     * token 过期时间
     */
    @Value(value = "${token.expire:54000}")
    private int expire;

    @Value(value = "${user.username:zhangsa456n}")
    private String username;

    @Autowired
    private StringRedisTemplate redisTemplate;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //获取请求
        ServerHttpRequest request = exchange.getRequest();
        //获取响应
        ServerHttpResponse response = exchange.getResponse();

        //获取请求路径
        String path = request.getURI().getPath();


        if (CodeFilter.noHasAuthorize(path)) {
            //如果包含了登录了路径,则放行
            return chain.filter(exchange);
        }

        //获取请求头
        HttpHeaders requestHeaders = request.getHeaders();
        HttpHeaders responseHeaders = response.getHeaders();

        //从请求头中获取令牌
        String token = requestHeaders.getFirst(AUTHORIZE_TOKEN);

        if (StringUtils.isEmpty(token)) {
            //无权限统一返回码的封装
            return getResultInfo(response);
        }

        token = StringUtils.substring(token, 7);

        Object userId = null;
        Object userName = null;
        Object organizationId = null;

        try {
            //如果有令牌,则解析令牌
            Claims claims = JwtUtil.parseJWT(token);

            userId = claims.get("userId");
            userName = claims.get("username");
            organizationId = claims.get("organizationId");
            Object orgList = claims.get("orgList");
            Object deptId = claims.get("deptId");
            //校验过期时间
            Date expiration = claims.getExpiration();

            //超管放行
            if (StringUtils.equalsIgnoreCase(String.valueOf(userName), username)) {
                getRequestInfo(request, token, userId, userName, organizationId, orgList, deptId);
                return chain.filter(exchange);
            }

            //需要认证不需要权限
            if (WhitelistCodeFilter.noHasAuthorize(path)){
                getRequestInfo(request, token, userId, userName, organizationId, orgList, deptId);
                return chain.filter(exchange);
            }

            //封装请求参数
            Map<String, Object> infoMap = getStringObjectMap(userId, userName, organizationId, orgList, deptId);

            //刷新token
            Mono<Void> buffer = getRefTokenInfo(response, userId, userName, expiration, infoMap);
            if (buffer != null) {
                return buffer;
            }

            Object codeInfoMapInfo = redisTemplate.opsForHash().entries(String.valueOf(userId));

            Map<String, String> codeInfoMap = JSONObject.parseObject(JSONObject.toJSONString(codeInfoMapInfo), Map.class);

            if (codeInfoMap.size() == 0) {
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                //无权限统一返回码
                return getResultInfo(response);
            }

            Set<Map.Entry<String, String>> entries = codeInfoMap.entrySet();
            for (Map.Entry<String, String> entry : entries) {
                String key = entry.getKey();
                String value = entry.getValue();

                if (value.contains(",")) {
                    final String[] valueString = value.split(",");
                    for (String var : valueString) {
                        if (StringUtils.equalsIgnoreCase(path, var)) {
                            return getVoidMono(responseHeaders, exchange, chain, request, response, path, token, userId, userName, organizationId, orgList, var, deptId);
                        }
                    }
                    //无权限统一返回码
                    return getResultInfo(response);

                } else {
                    return getVoidMono(responseHeaders, exchange, chain, request, response, path, token, userId, userName, organizationId, orgList, value, deptId);
                }

            }

        } catch (Exception e) {
            log.error("令牌解析异常{} ", e.toString(), e);
            return getResultInfo(response);
        }

        return chain.filter(exchange);
    }

    /**
     * 请求参数封装
     *
     * @param request, token, userId, userName, organizationId, orgList, deptId
     * @return void
     * @author ZhouNing
     * @date 2021/6/17
     */
    private void getRequestInfo(ServerHttpRequest request, String token, Object userId, Object userName, Object organizationId, Object orgList, Object deptId) {
        request.mutate().header("token", token);
        request.mutate().header("userName", String.valueOf(userName));
        request.mutate().header("userId", String.valueOf(userId));
        request.mutate().header("organizationId", String.valueOf(organizationId));
        request.mutate().header("orgList", String.valueOf(orgList));
        request.mutate().header("deptId", String.valueOf(deptId));
    }


    /**
     * @param response, userId, userName, expiration, infoMap]
     * @return Mono<java.lang.Void>
     * @author ZhouNing
     * @date 2021/6/16
     * 刷新token
     */

    private Mono<Void> getRefTokenInfo(ServerHttpResponse response, Object userId, Object userName, Date expiration, Map<String, Object> infoMap) {
        if (expiration.getTime() - System.currentTimeMillis() < expire) {

            String refToken = JwtUtil.createJWT(String.valueOf(userId), String.valueOf(userName), null, infoMap);
            JSONObject message = new JSONObject();
            message.put("code", -1);
            message.put("msg", "error");
            message.put("token", "Bearer " + refToken);
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            byte[] bits = message.toJSONString().getBytes(StandardCharsets.UTF_8);
            DataBuffer buffer = response.bufferFactory().wrap(bits);
            response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
            return response.writeWith(Mono.just(buffer));

        }
        return null;
    }

    /**
     * @author ZhouNing
     * 抽取统一无权限返回参数封装
     * @date 2021/6/15
     */

    private Mono<Void> getResultInfo(ServerHttpResponse response) {
        JSONObject message = new JSONObject();
        message.put("code", 401);
        message.put("msg", "error");
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        byte[] bits = message.toJSONString().getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }


    /**
     * @param userId, userName, organizationId, orgList, deptId
     * @return map
     * @author ZhouNing
     * @date 2021/6/15
     */

    private Map<String, Object> getStringObjectMap(Object userId, Object userName, Object organizationId, Object orgList, Object deptId) {
        Map<String, Object> infoMap = new HashMap<>(16);
        infoMap.put("userId", userId);
        infoMap.put("username", userName);
        infoMap.put("organizationId", organizationId);
        infoMap.put("orgList", orgList);
        infoMap.put("deptId", deptId);
        return infoMap;
    }

    /**
     * @author ZhouNing
     * 抽取公共方法,校验权限
     * @date 2021/6/15
     */
    private Mono<Void> getVoidMono(HttpHeaders responseHeaders, ServerWebExchange exchange, GatewayFilterChain chain, ServerHttpRequest request, ServerHttpResponse response, String path, String token, Object userId, Object userName, Object organizationId, Object orgList, String value, Object deptId) {
        if (StringUtils.equalsIgnoreCase(path, value)) {

            request.mutate().header("token", token);
            request.mutate().header("userName", String.valueOf(userName));
            request.mutate().header("userId", String.valueOf(userId));
            request.mutate().header("organizationId", String.valueOf(organizationId));
            request.mutate().header("orgList", String.valueOf(orgList));
            request.mutate().header("deptId", String.valueOf(deptId));
            //校验通过，并且有查看权限才可以查看
            return chain.filter(exchange);

        } else {
            //统一返回参数封装
            return getResultInfo(response);
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

